#pragma once

#include "znx_conn.h"
#include "comm/znx_time.h"
#include "comm/znx_pool.h"
#include "comm/znx_cleanup.h"
#include "comm/znx_thread_base.h"

// process exit flag.
extern volatile znx_bool_t      proc_exit;

typedef struct znx_event_loop_data_s    znx_event_loop_data_t;

typedef void (*znx_event_loop_handle_pt)(znx_event_loop_data_t *loop_data);


struct znx_event_loop_data_s {
    znx_event_loop_handle_pt            init_handle;
    znx_event_loop_handle_pt            proc_exit_handle;
    znx_event_loop_handle_pt            quit_handle;

    ////////// External queue /////////////
    znx_spinlock_t                      lock;
    znx_queue_t                         external_queue;

    //////////// Epoll attribute //////////
    int                                 epfd;
    znx_conn_t                          notify_conn;        // for wakeup epoll wait
    znx_epoll_event_t                   *event_list;

    ///////////// Local queue /////////////
    // Local queue is a lock-free queue.
    // Move tasks in external queue to this queue at one time
    // This can reduce the contention of external queue.
    znx_queue_t                         local_queue;

    // It's a rbtree, for timer task.
    znx_rbtree_t                        timer_rbtree;
    znx_rbtree_node_t                   timer_sentinel;

    // It's a ready queue.
    znx_queue_t                         ready_queue;

    // for event loop quit.
    void                                *data;

    znx_cleanup_list_t                  cleanup_list;

    // for graceful shutdown.
    // used to record async_tasks initiated by the worker loop.
    uint64_t                            async_tasks;
    znx_atomic_bool_t                   can_stop;
    znx_atomic_bool_t                   quit;
    znx_atomic_bool_t                   stopped;

    unsigned                            stop:1;
};


static inline void
znx_event_loop_data_init(znx_event_loop_data_t *loop_data,
    znx_event_loop_handle_pt init_handle,
    znx_event_loop_handle_pt proc_exit_handle,
    znx_event_loop_handle_pt quit_handle)
{
    loop_data->init_handle = init_handle;
    loop_data->proc_exit_handle = proc_exit_handle;
    loop_data->quit_handle = quit_handle;

    znx_spinlock_init(&loop_data->lock);
    znx_queue_init(&loop_data->external_queue);

    loop_data->epfd = INVALID_FD;
    znx_conn_init(&loop_data->notify_conn, ZNX_CONN_EPOLL_LT);

    loop_data->event_list = NULL;

    znx_queue_init(&loop_data->local_queue);

    znx_rbtree_init(&loop_data->timer_rbtree, &loop_data->timer_sentinel,
        znx_event_timer_cmp);

    znx_queue_init(&loop_data->ready_queue);
    znx_cleanup_list_init(&loop_data->cleanup_list);
}


void *znx_event_loop_runner(znx_thread_base_t *thread_base);
void znx_event_loop_quit(znx_thread_base_t *thread_base);

// external task delivery
void znx_event_loop_post_event(znx_thread_base_t *thread_base,
    znx_dispatch_event_t *dispatch_event);


// thread local API's
void znx_event_loop_add_timer(znx_event_t *event, int64_t timeout_ms);
void znx_event_loop_del_timer(znx_event_t *event);

// thread local API's
static inline znx_cleanup_item_t *
znx_event_loop_cleanup_add(znx_cleanup_handler_pt handler, void *data)
{
    znx_event_loop_data_t *loop_data = current_thread_base->data;
    return znx_cleanup_list_add(&loop_data->cleanup_list, handler, data);
}


static inline void
znx_event_loop_cleanup_del(znx_cleanup_item_t *item)
{
    znx_event_loop_data_t *loop_data = current_thread_base->data;
    return znx_cleanup_list_del(&loop_data->cleanup_list, item);
}


// thread local API's
znx_bool_t znx_event_loop_start_io(znx_conn_t *conn);
znx_bool_t znx_event_loop_stop_io(znx_conn_t *conn);
znx_bool_t znx_event_loop_enable_read(znx_conn_t *conn);
znx_bool_t znx_event_loop_disable_read(znx_conn_t *conn);
znx_bool_t znx_event_loop_enable_write(znx_conn_t *conn);
znx_bool_t znx_event_loop_disable_write(znx_conn_t *conn);
// close io and clean up all timers for this conn
void znx_event_loop_remove_conn(znx_conn_t *conn);


// for graceful shutdown.

static inline void
znx_event_loop_async_task_incr()
{
    znx_event_loop_data_t *loop_data = current_thread_base->data;
    loop_data->async_tasks++;
}


static inline void
znx_event_loop_async_task_decr()
{
    znx_event_loop_data_t *loop_data = current_thread_base->data;
    loop_data->async_tasks--;
}

static inline void
znx_event_loop_remove_conn_handle(void *data)
{
    znx_conn_t *conn = (znx_conn_t *)data;
    znx_event_loop_remove_conn(conn);
}
